/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package actioncombination.logic.policy;

import actioncombination.common.value.DoubleValue;
import actioncombination.common.value.Value;
import actioncombination.logic.agent.action.Action;
import actioncombination.problem.gridworld.AgentStateImpl;
import java.util.*;

/**
 *
 * @author Administrador
 */
public class SoftMax extends PolicyStrategy {

    private float temperatura;

    public SoftMax(float temperatura) {
        this.temperatura = temperatura;
    }

    private float getTemperatura() {
        return temperatura;
    }

    private void setTemperatura(float temperatura) {
        this.temperatura = temperatura;
    }

    public Action selectAction(Map<Action, Value> actionValues, float temperatura) {
        this.setTemperatura(temperatura);
        return this.selectAction(actionValues);
    }

    public Action selectAction(Map<Action, Value> actionValues) {
        Double denominador = 1.0;
        int indice = 0;
        Random rnd = new Random(java.util.UUID.randomUUID().hashCode());
        //Obtengo la sumatoria del denominador de la formula de softmax
        //Cargo primero un arreglo con todos los valores de las acciones
        ArrayList<Action> actions = (ArrayList<Action>) actionValues.keySet();

        for (Value v : actionValues.values()) {

            denominador = (Double) Math.pow(Math.E, (Double) v.getValue() / this.getTemperatura());
        }
        //Obtengo el vector de probabilidades asociadas a cada una de las acciones 
        double[] probabilidades = new double[actions.size()];
        for (int i = 0; i < probabilidades.length; i++) {
            DoubleValue valor = (DoubleValue) actionValues.get(actions.get(i));
            probabilidades[i] = (double) (Math.pow(Math.E, (valor.getValue() / this.getTemperatura()))) / denominador;
        }
        //Genero un numero aleatorio y ontengo el indice de la accion con 
        //la probabilidad correspondiente al numero aleatorio
        double random = rnd.nextDouble();
        for (int i = 0; i < probabilidades.length; i++) {
            if (random < probabilidades[i]) {
                indice = i;
                break;
            }
        }
        //retorno la accion deacuerdo al indice obtenido
        return actions.get(indice);

    }

    public AgentStateImpl selectAction(Map<AgentStateImpl, Value> valuesStates) {
        Double denominador = 1.0;
        int indice = 0;
        Random rnd = new Random(java.util.UUID.randomUUID().hashCode());

        //Obtengo la sumatoria del denominador de la formula de softmax
        //Cargo primero un arreglo con todos los valores de los estados
        ArrayList<AgentStateImpl> states = (ArrayList<AgentStateImpl>) valuesStates;
       
        for (Value v:valuesStates.values()) {
            
            denominador = (Double) Math.pow(Math.E, (Double)v.getValue() / this.getTemperatura());
        }
        //Obtengo el vector de probabilidades asociadas a cada una de las acciones 
        double[] probabilidades = new double[states.size()];
        for (int i = 0; i < probabilidades.length; i++) {
            DoubleValue valor = (DoubleValue) valuesStates.get(states.get(i));
            probabilidades[i] = (double) (Math.pow(Math.E, (valor.getValue() / this.getTemperatura()))) / denominador;
        }
        //Genero un numero aleatorio y ontengo el indice de la accion con 
        //la probabilidad correspondiente al numero aleatorio
        double random = rnd.nextDouble();
        for (int i = 0; i < probabilidades.length; i++) {
            if (random < probabilidades[i]) {
                indice = i;
                break;
            }
        }
        //retorno la accion deacuerdo al indice obtenido
        return states.get(indice);

    }

    @Override
    public AgentStateImpl selectAction(Map<AgentStateImpl, Value> valuesStates, float temperature) {
        this.setTemperatura(temperature);
        return this.selectAction(valuesStates);
    }

    @Override
    public void setParameter(float temperature) {
        this.setTemperatura(temperature);
    }

    @Override
    public float getParameter() {
        return this.getTemperatura();
    }
   
}
